// 有限状态机
const EOF = Symbol("EOF");
import {
  CommaToken,
  DataToken,
  EOFToken,
  FunctionToken,
  LexerToken,
  NumberToken,
  OperatorToken,
  ParEndToken,
  ParStartToken,
} from "./lexerToken";

export type LexerConfig = {
  fncs?: string[];
  datas?: string[];
};
// 词法分析
class Lexer {
  token: Array<string>;
  tokens: Array<LexerToken>;
  state: (char?: any, at?: number) => any;
  fncs: string[] = [];
  datas: string[] = [];

  startAt: number = 0;
  constructor(config?: LexerConfig) {
    const { fncs = [], datas = [] } = config || {};
    this.token = [];
    this.tokens = [];
    this.state = this.start;
    this.fncs = fncs;
    this.datas = datas;
  }

  start(char: any, start?: number): any {
    if (start !== undefined) this.startAt = start;
    if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(char)) {
      this.token.push(char);
      return this.int;
    }

    if (char === ".") {
      this.token.push(char);
      return this.inFloat;
    }

    if (["+", "-"].includes(char)) {
      this.emmitToken(new OperatorToken(char, 2));
      return this.start;
    }

    if (["*", "/"].includes(char)) {
      this.emmitToken(new OperatorToken(char, 3));
      return this.start;
    }

    if (char === "(") {
      this.emmitToken(new ParStartToken());
      return this.start;
    }

    if (char === ")") {
      this.emmitToken(new ParEndToken());
      return this.start;
    }

    if (char === EOF) {
      this.emmitToken(new EOFToken(char));
      return this.start;
    }

    if (char === "@") {
      this.token.push(char);
      return this.inFunction;
    }

    if (char === "#") {
      this.token.push(char);
      return this.inData;
    }

    if (char === ",") {
      this.emmitToken(new CommaToken(char));
      return this.start;
    }

    // 空白字符
    if ([" ", "\r", "\n"].includes(char)) {
      return this.start;
    }

    throw new Error("Unexpected character");
  }

  int(char: any, start?: number): any {
    if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(char)) {
      this.token.push(char);
      return this.int;
    } else if (char === ".") {
      this.token.push(char);
      return this.inFloat;
    } else {
      this.emmitToken(new NumberToken(this.token.join("")));
      this.token = [];
      return this.start(char, start); // put back char
    }
  }

  inFloat(char: any, start?: number) {
    if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(char)) {
      this.token.push(char);
      return this.inFloat;
    } else if (char === ".") {
      throw new Error("不能出现`..`");
    } else {
      if (this.token.length === 1 && this.token[0] === ".") {
        throw new Error("不能单独出现`.`");
      }
      this.emmitToken(new NumberToken(this.token.join("")));
      this.token = [];
      return this.start(char, start); // put back char
    }
  }

  inFunction(char: any) {
    /**
     * @ABCD_EF(
     * @A1BC3_2(
     */
    if (char == "(") {
      // 验证函数名称是否合法
      const fncName = this.token.join("").substring(1);
      if (!this.isValidateFnc(fncName)) {
        throw new Error(`函数名称${fncName}不合法`);
      }
      this.emmitToken(new FunctionToken(fncName));
      this.token = [];
      return this.start;
    } else {
      if (char === EOF) {
        throw new Error("输入错误");
      } else if (/d+|\-|\_/.test(char)) {
        if (this.token.length == 1) {
          throw new Error("函数名错误");
        }
        return this.inFunction;
      } else if (/[a-zA-Z]/.test(char)) {
        this.token.push(char);
        return this.inFunction;
      }
      throw new Error("函数名错误");
    }
  }

  inData(char: any) {
    if (char !== EOF && /d+|\-|\_/g.test(char)) {
      if (this.token.length == 1) {
        throw new Error("数据源名错误");
      }
      this.token.push(char);
      return this.inData;
    } else if (char !== EOF && /[a-zA-Z]/.test(char)) {
      this.token.push(char);
      return this.inData;
    } else {
      const dataName = this.token.join("").substring(1);
      if (!this.isValidateData(dataName)) {
        throw new Error(`数据源名称${dataName}不合法`);
      }
      this.emmitToken(new DataToken(dataName));
      this.token = [];
      return this.start(char);
    }
  }

  emmitToken(token: LexerToken) {
    this.tokens.push(token);
  }

  isValidateFnc(fn: string) {
    return this.fncs.includes(fn);
  }

  isValidateData(dataName: string) {
    return this.datas.includes(dataName);
  }

  push(char: any, start: number) {
    this.state = this.state(char, start);
    return this.check();
  }
  end() {
    this.state(EOF);
    return this.check();
  }

  check() {
    // 检测是否有 token 生成并返回。
    const _token = [...this.tokens];
    this.tokens = [];
    return _token;
  }

  clear() {
    this.token = [];
    this.tokens = [];
    this.startAt = 0;
    this.state = this.start;
  }
}

export default Lexer;
